package com.my12306.common.util;

import com.google.gson.Gson;
import com.my12306.common.constant.RedisConstants;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @Author:苏牧夕
 * @Date:2020/5/12 18:42
 * @Version 1.0
 */
@Component

public class RedisUtils {
    private static Gson gson = new Gson();
    private static Random random = new Random();
    @Autowired
    RedisTemplate<String, Object> redisTemplate;
    @Autowired
    ValueOperations<String, Object> valueOperations;
    @Autowired
    SetOperations<String, Object> setOperations;
    @Autowired
    HashOperations<String, String, Object> hashOperations;
    @Autowired
    ZSetOperations<String, Object> zSetOperations;
    @Autowired
    ListOperations<String, Object> listOperations;

    /**
     * 生成缓存key
     *
     * @param prefix
     * @param suffix
     * @return
     */
    public static String getKeyPrefix(String prefix, String className, String suffix) {
        StringBuilder builder = new StringBuilder();
        builder.append(prefix).append(className).append(suffix);
        return builder.toString();
    }

    /**
     * 生成key
     * @param key
     * @return
     */
    /**
     * 对象转json字符串
     *
     * @param object
     * @return
     */
    private static String ObjectToJson(Object object) {
        if (object instanceof Integer || object instanceof String || object instanceof Float || object instanceof Double
                || object instanceof Boolean || object instanceof Long)
            return String.valueOf(object);
        return gson.toJson(object);
    }

    /**
     * 根据字符串和对象类型对象
     *
     * @param object    字符串对象
     * @param classType 对象类型
     * @param <T>
     * @return
     */
    private static <T> T JsonToObject(String object, Class<T> classType) {
        return gson.fromJson(object, classType);
    }

    /**
     * 缓存字符串类型 无有效时间使用默认有效时间
     *
     * @param key
     * @param value
     */
    public void set(String key, Object value) {
        set(key, value, RedisConstants.DEFAULT_EXPIRE);
    }

    /**
     * 缓存字符串类型 带有效时间
     *
     * @param key
     * @param value
     * @param expire
     */
    public void set(String key, Object value, long expire) {
        valueOperations.set(key, value);
        setExpire(key, expire);
    }

    /**
     * 缓存字符串类型 带有效时间 和单位
     *
     * @param key
     * @param value
     * @param expire
     */
    public void set(String key, Object value, long expire, TimeUnit timeUnit) {
        valueOperations.set(key, value);
        setExpire(key, expire, timeUnit);
    }

    /**
     * 判断key是否存在
     *
     * @param key
     * @return
     */
    public Boolean hasKey(String key) {
        if (key == null) return false;
        return redisTemplate.hasKey(key);
    }

    /**
     * 根据key 获取对应的字符串value
     *
     * @param key
     * @return
     */
    public Object get(String key) {
        return get(key, RedisConstants.NON_EXPIRE);
    }

    /**
     * 根据key，获取对应的字符串value 并设置有效时间
     *
     * @param key
     * @param expire
     * @return
     */
    public Object get(String key, long expire) {
        Object value = valueOperations.get(key);
        setExpire(key, expire);
        return value;
    }

    /**
     * 获取list类型的缓存
     * 有效时间2天
     *
     * @param key
     * @param type
     * @return
     */
    public Object get(String key, Long expire, Type type) {
        Object value = valueOperations.get(key);
        if (expire != null) {
            setExpire(key, expire, TimeUnit.DAYS);
        }
        return value;
    }


    /**
     * 根据key 删除value
     *
     * @param key
     * @return
     */
    public boolean delete(String key) {
        return delete(Arrays.asList(key));
    }

    /**
     * 批量删除 keys
     *
     * @param keys
     * @return
     */
    public boolean delete(List<String> keys) {
        return redisTemplate.delete(keys) > 0 ? true : false;
    }

    /**
     * 根据 key的前缀删除，如果是null，则全部清除
     *
     * @param keys
     * @return
     */
    public boolean deleteKeys(String keys) {
        if (StringUtils.isEmpty(keys.trim())) {
            return deleteAll(redisTemplate.keys("*"));
        }
        return deleteAll(redisTemplate.keys(keys + "*"));
    }

    /**
     * 清楚所有缓存
     *
     * @return
     */
    private boolean deleteAll(Set<String> keys) {
        return redisTemplate.delete(keys) > 0 ? true : false;
    }

    /**
     * 根据key 自减 value 默认减1 适用于 int\long类型
     *
     * @param key
     * @return
     */
    public <T> T decreate(String key) {
        return (T) decreate(key, RedisConstants.INCREATE_VALUE);
    }

    /**
     * 根据key和number 让value 自减 number 适用于 int\long类型
     *
     * @param key
     * @param number
     * @return
     */
    public Long decreate(String key, long number) {
        return valueOperations.decrement(key, number);
    }

    /**
     * 根据key 让value自增 1 适用于 int\long类型
     *
     * @param key
     * @return
     */
    public Long increate(String key) {
        return increate(key, RedisConstants.INCREATE_VALUE);
    }

    /**
     * 根据key 让value自增 number  适应用int \ long类型
     *
     * @param key
     * @param number
     * @return
     */
    public Long increate(String key, Long number) {
        return valueOperations.increment(key, number);
    }

    /**
     * 根据key 让value自增 number double
     *
     * @param key
     * @param number
     * @return
     */
    public Double increate(String key, Double number) {
        return valueOperations.increment(key, number);
    }

    /**
     * 存储 set集合类 k-v
     *
     * @param key
     * @param object
     */
    public void setSet(String key, Object object) {
        setSet(key, object, RedisConstants.DEFAULT_EXPIRE);
    }

    /**
     * 存储集合类set 如果集合已经存在在直接添加对应的value
     * 如果value 也存在则不添加
     *
     * @param key
     * @param object
     * @param expire
     */
    public void setSet(String key, Object object, long expire) {
        setOperations.add(key, object);
        setExpire(key, expire);
    }

    /**
     * 获取集合中所有的元素
     *
     * @param key
     * @return
     */
    public Set<?> getSetAll(String key) {
        Set<Object> members = setOperations.members(key);
        return members;
    }

    /**
     * 根据key 从集合里弹出一个元素
     *
     * @param key
     * @return
     */
    public Object getSet(String key) {
        String pop = (String) setOperations.pop(key);
        return pop;
    }

    /**
     * 根据key 随机获取一个value
     *
     * @param key
     * @return
     */
    public Object getRandomSet(String key) {
        String value = (String) setOperations.randomMember(key);
        return value;
    }

    /**
     * 返回key 与otherkey的差集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<?> getDifference(String key, String otherKey) {
        Set<Object> difference = setOperations.difference(key, otherKey);
        return difference;
    }

    /**
     * 返回 key 和 otherKey的交集
     *
     * @param key
     * @param otherKey
     */
    public Set<?> getIntersect(String key, String otherKey) {
        Set<Object> intersect = setOperations.intersect(key, otherKey);
        return intersect;
    }

    /**
     * 根据key 删除元素
     *
     * @param key
     * @return
     */
    public boolean deleteSet(String key) {
        return setOperations.remove(key) > 0 ? true : false;
    }

    /**
     * 根据key 删除指定的元素
     *
     * @param key
     * @return
     */
    public boolean deleteSet(String key, List<?> list) {
        return setOperations.remove(key, list) > 0 ? true : false;
    }

    public void setZset(String key, Object object, double scope) {
        setZset(key, object, scope, RedisConstants.DEFAULT_EXPIRE);
    }

    /**
     * 存储一个key-value 进有序集合，会根据scope排序
     *
     * @param key
     * @param object
     * @param scope
     * @param expire
     */
    public void setZset(String key, Object object, double scope, long expire) {
        zSetOperations.add(key, object, scope);
        setExpire(key, expire);
    }

    /**
     * 根据key 和value 来获取 成绩
     *
     * @param key
     * @param value
     * @return
     */
    public Double getZset(String key, Object value) {
        return zSetOperations.score(key, value);
    }

    /**
     * 根据key找集合中的 value然后 增加 它的成绩 默认是1
     *
     * @param key
     * @param value
     * @return
     */
    public Double increateScore(String key, Object value) {
        return zSetOperations.incrementScore(key, value, RedisConstants.DECREATE_VALUE);
    }

    /**
     * 根据key找集合中的 value然后 增加 它的成绩 scopte
     *
     * @param key
     * @param value
     * @param scope
     * @return
     */
    public Double increateScore(String key, Object value, Integer scope) {
        return zSetOperations.incrementScore(key, value, scope);
    }

    /**
     * 获取 排名在start ~ end直接的元素
     *
     * @param key
     * @param start
     * @param end
     */
    public Set<?> getZsetRange(String key, int start, int end) {
        return zSetOperations.reverseRangeWithScores(key, start, end);
    }

    /**
     * @param key-集合key
     * @param value     -需要从排行榜移除的value的元素
     * @return
     */
    public Long remove(String key, Object value) {
        return redisTemplate.opsForZSet().remove(key, value);
    }

    /**
     * 设置key的有效时间
     *
     * @param key
     * @param expire
     */
    public void setExpire(String key, long expire) {
        if (expire != RedisConstants.NON_EXPIRE) {
            redisTemplate.expire(key, ramdomExpire(expire), TimeUnit.SECONDS);
        }
    }

    /**
     * 设置key的有效时间
     * 不生成随机时间，并且单位可自动设置
     *
     * @param key
     * @param expire
     */
    public void setExpire(String key, long expire, TimeUnit timeUnit) {
        if (expire != RedisConstants.NON_EXPIRE) {
            redisTemplate.expire(key, expire, timeUnit);
        }
    }

    /**
     * 生成一个随机数，防止出现缓存雪崩
     *
     * @param expire
     * @return
     */
    private long ramdomExpire(long expire) {
        expire = random.nextInt(RedisConstants.RANDOM_FACTHOR) * (random.nextInt(10) + 1) + expire;
        return expire;
    }
}
